/*
 * SPDX-FileCopyrightText: 2023 UnionTech Software Technology Co., Ltd.
 *
 * SPDX-License-Identifier: MulanPSL-2.0
 */
#![allow(
    dead_code,
    mutable_transmutes,
    non_camel_case_types,
    non_snake_case,
    non_upper_case_globals,
    unused_assignments,
    unused_mut,
    clashing_extern_declarations,
    clippy::if_same_then_else
)]
use crate::common::*;
extern "C" {
    static mut stdin: *mut FILE;
    fn fclose(__stream: *mut FILE) -> libc::c_int;
    fn asprintf(__ptr: *mut *mut libc::c_char, __fmt: *const libc::c_char, _: ...) -> libc::c_int;
    fn getc(__stream: *mut FILE) -> libc::c_int;
    fn ungetc(__c: libc::c_int, __stream: *mut FILE) -> libc::c_int;
    fn getdelim(
        __lineptr: *mut *mut libc::c_char,
        __n: *mut size_t,
        __delimiter: libc::c_int,
        __stream: *mut FILE,
    ) -> __ssize_t;
    fn strtod(_: *const libc::c_char, _: *mut *mut libc::c_char) -> libc::c_double;
    fn malloc(_: libc::c_ulong) -> *mut libc::c_void;
    fn calloc(_: libc::c_ulong, _: libc::c_ulong) -> *mut libc::c_void;
    fn realloc(_: *mut libc::c_void, _: libc::c_ulong) -> *mut libc::c_void;
    fn reallocarray(__ptr: *mut libc::c_void, __nmemb: size_t, __size: size_t)
        -> *mut libc::c_void;
    fn free(_: *mut libc::c_void);
    fn qsort(__base: *mut libc::c_void, __nmemb: size_t, __size: size_t, __compar: __compar_fn_t);
    fn strcasecmp(_: *const libc::c_char, _: *const libc::c_char) -> libc::c_int;
    fn strcmp(_: *const libc::c_char, _: *const libc::c_char) -> libc::c_int;
    fn strdup(_: *const libc::c_char) -> *mut libc::c_char;
    fn memcpy(_: *mut libc::c_void, _: *const libc::c_void, _: libc::c_ulong) -> *mut libc::c_void;
    fn strncasecmp(_: *const libc::c_char, _: *const libc::c_char, _: libc::c_ulong)
        -> libc::c_int;
    fn strlen(_: *const libc::c_char) -> libc::c_ulong;
    fn __ctype_b_loc() -> *mut *const libc::c_ushort;
    fn free_members(members: *mut member_list);
    fn free_privilege(priv_0: *mut privilege);
    fn free_parse_tree(parse_tree: *mut sudoers_parse_tree);
    fn sudo_warnx_nodebug_v1(fmt: *const libc::c_char, _: ...);
    fn sudo_fatalx_nodebug_v1(fmt: *const libc::c_char, _: ...) -> !;
    fn sudo_warn_gettext_v1(
        domainname: *const libc::c_char,
        msgid: *const libc::c_char,
    ) -> *mut libc::c_char;
    fn base64_decode(str: *const libc::c_char, dst: *mut libc::c_uchar, dsize: size_t) -> size_t;
    static mut sudoers_subsystem_ids: [libc::c_uint; 0];
    fn sudo_debug_printf2_v1(
        func: *const libc::c_char,
        file: *const libc::c_char,
        line: libc::c_int,
        level: libc::c_int,
        fmt: *const libc::c_char,
        _: ...
    );
    fn sudo_ldap_is_negated(valp: *mut *mut libc::c_char) -> bool;
    fn sudo_ldap_parse_option(
        optstr: *mut libc::c_char,
        varp: *mut *mut libc::c_char,
        valp: *mut *mut libc::c_char,
    ) -> libc::c_int;
    fn sudo_ldap_role_to_priv(
        cn: *const libc::c_char,
        hosts: *mut libc::c_void,
        runasusers: *mut libc::c_void,
        runasgroups: *mut libc::c_void,
        cmnds: *mut libc::c_void,
        opts: *mut libc::c_void,
        notbefore: *const libc::c_char,
        notafter: *const libc::c_char,
        warnings: bool,
        store_options: bool,
        iter: sudo_ldap_iter_t,
    ) -> *mut privilege;
    fn rbinsert(_: *mut rbtree, _: *mut libc::c_void, _: *mut *mut rbnode) -> libc::c_int;
    fn rbcreate(
        _: Option<unsafe extern "C" fn(*const libc::c_void, *const libc::c_void) -> libc::c_int>,
    ) -> *mut rbtree;
    fn rbdestroy(_: *mut rbtree, _: Option<unsafe extern "C" fn(*mut libc::c_void) -> ()>);
    fn str_list_alloc() -> *mut sudoers_str_list;
    fn str_list_free(v: *mut libc::c_void);
    fn sudoers_string_alloc(s: *const libc::c_char) -> *mut sudoers_string;
}

pub const DEFAULTS: libc::c_int = 265;

#[derive(Copy, Clone)]
#[repr(C)]
pub struct sudo_role {
    pub entries: C2RustUnnamed_7,
    pub cn: *mut libc::c_char,
    pub notbefore: *mut libc::c_char,
    pub notafter: *mut libc::c_char,
    pub order: libc::c_double,
    pub cmnds: *mut sudoers_str_list,
    pub hosts: *mut sudoers_str_list,
    pub users: *mut sudoers_str_list,
    pub runasusers: *mut sudoers_str_list,
    pub runasgroups: *mut sudoers_str_list,
    pub options: *mut sudoers_str_list,
}
#[derive(Copy, Clone)]
#[repr(C)]
pub struct sudoers_str_list {
    pub stqh_first: *mut sudoers_string,
    pub stqh_last: *mut *mut sudoers_string,
    pub refcnt: libc::c_uint,
}
#[derive(Copy, Clone)]
#[repr(C)]
pub struct sudoers_string {
    pub entries: C2RustUnnamed_6,
    pub str_0: *mut libc::c_char,
}
#[derive(Copy, Clone)]
#[repr(C)]
pub struct C2RustUnnamed_6 {
    pub stqe_next: *mut sudoers_string,
}
#[derive(Copy, Clone)]
#[repr(C)]
pub struct C2RustUnnamed_7 {
    pub stqe_next: *mut sudo_role,
}
#[derive(Copy, Clone)]
#[repr(C)]
pub struct sudo_role_list {
    pub stqh_first: *mut sudo_role,
    pub stqh_last: *mut *mut sudo_role,
}

pub type sudo_ldap_iter_t =
    Option<unsafe extern "C" fn(*mut *mut libc::c_void) -> *mut libc::c_char>;
pub type __compar_fn_t =
    Option<unsafe extern "C" fn(*const libc::c_void, *const libc::c_void) -> libc::c_int>;
#[macro_export]
macro_rules! _ISalpha {
    () => {
        _ISbit!(2)
    };
}
#[macro_export]
macro_rules! isalpha {
    ($c:expr) => {
        __isctype!(($c), _ISalpha!())
    };
}

fn sudo_role_free(mut role: *mut sudo_role) {
    unsafe {
        debug_decl!(SUDOERS_DEBUG_UTIL!());

        if !role.is_null() {
            free((*role).cn as *mut libc::c_void);
            free((*role).notbefore as *mut libc::c_void);
            free((*role).notafter as *mut libc::c_void);
            str_list_free((*role).cmnds as *mut libc::c_void);
            str_list_free((*role).hosts as *mut libc::c_void);
            str_list_free((*role).users as *mut libc::c_void);
            str_list_free((*role).runasusers as *mut libc::c_void);
            str_list_free((*role).runasgroups as *mut libc::c_void);
            str_list_free((*role).options as *mut libc::c_void);
            free(role as *mut libc::c_void);
        }

        debug_return!();
    } //unsaafe
}

fn sudo_role_alloc() -> *mut sudo_role {
    let mut role: *mut sudo_role = std::ptr::null_mut::<sudo_role>();
    unsafe {
        debug_decl!(SUDOERS_DEBUG_UTIL!());

        role = calloc(
            1 as libc::c_int as libc::c_ulong,
            ::core::mem::size_of::<sudo_role>() as libc::c_ulong,
        ) as *mut sudo_role;
        if !role.is_null() {
            (*role).cmnds = str_list_alloc();
            (*role).hosts = str_list_alloc();
            (*role).users = str_list_alloc();
            (*role).runasusers = str_list_alloc();
            (*role).runasgroups = str_list_alloc();
            (*role).options = str_list_alloc();
            if ((*role).cmnds).is_null()
                || ((*role).hosts).is_null()
                || ((*role).users).is_null()
                || ((*role).runasusers).is_null()
                || ((*role).runasgroups).is_null()
                || ((*role).options).is_null()
            {
                sudo_role_free(role);
                role = std::ptr::null_mut::<sudo_role>();
            }
        }

        debug_return_ptr!(role);
    } //unsafe
}

/*
 * Parse an LDIF line, filling in attribute name and value.
 * Modifies line, decodes base64 attribute values if present.
 * See http://www.faqs.org/rfcs/rfc2849.html
 */
fn ldif_parse_attribute(
    mut line: *mut libc::c_char,
    mut name: *mut *mut libc::c_char,
    mut value: *mut *mut libc::c_char,
) -> bool {
    let mut encoded: bool = false;
    let mut attr: *mut libc::c_char = std::ptr::null_mut::<libc::c_char>();
    let mut cp: *mut libc::c_char = std::ptr::null_mut::<libc::c_char>();
    let mut ep: *mut libc::c_char = std::ptr::null_mut::<libc::c_char>();
    let mut colon: *mut libc::c_char = std::ptr::null_mut::<libc::c_char>();
    let mut len: size_t = 0;
    unsafe {
        debug_decl!(SUDOERS_DEBUG_UTIL!());

        /* Parse attribute name: [a-zA-Z][a-zA-Z0-9-]*: */
        if isalpha!(*line as libc::c_uchar as libc::c_int as isize) == 0 {
            debug_return_bool!(false);
        }
        cp = line.offset(1 as libc::c_int as isize);
        while *cp as libc::c_int != ':' as i32 && *cp as libc::c_int != '\0' as i32 {
            if isalnum!(*cp as libc::c_uchar as libc::c_int as isize) == 0
                && *cp as libc::c_int != '-' as i32
            {
                debug_return_bool!(false);
            }
            cp = cp.offset(1);
        }
        if *cp as libc::c_int != ':' as i32 {
            debug_return_bool!(false);
        }
        let fresh0 = cp;
        cp = cp.offset(1);
        colon = fresh0;

        /* Check for foo:: base64str. */
        if *cp as libc::c_int == ':' as i32 {
            encoded = true;
            cp = cp.offset(1);
        }

        /* Trim leading and trailing space. */
        while *cp as libc::c_int == ' ' as i32 {
            cp = cp.offset(1);
        }

        ep = cp.offset(strlen(cp) as isize);
        while ep > cp && *ep.offset(-(1 as libc::c_int) as isize) as libc::c_int == ' ' as i32 {
            ep = ep.offset(-1);
            /* Don't trim escaped trailing space if not base64. */
            if !encoded
                && ep != cp
                && *ep.offset(-(1 as libc::c_int) as isize) as libc::c_int == '\\' as i32
            {
                break;
            }
            *ep = '\0' as i32 as libc::c_char;
        }

        attr = cp;
        if encoded {
            /*
             * Decode base64 inline and add NUL-terminator.
             * The copy allows us to provide a useful message on error.
             */
            let mut copy: *mut libc::c_char = strdup(attr);
            if copy.is_null() {
                sudo_fatalx!(
                    b"%s: %s\0" as *const u8 as *const libc::c_char,
                    get_function_name!(),
                    b"unable to allocate memory\0" as *const u8 as *const libc::c_char
                );
            }
            len = base64_decode(attr, copy as *mut libc::c_uchar, strlen(copy));
            if len == -(1 as libc::c_int) as size_t {
                free(copy as *mut libc::c_void);
                debug_return_bool!(false);
            }
            memcpy(attr as *mut libc::c_void, copy as *const libc::c_void, len);
            *attr.offset(len as isize) = '\0' as i32 as libc::c_char;
            free(copy as *mut libc::c_void);
        }

        *colon = '\0' as i32 as libc::c_char;
        *name = line;
        *value = attr;

        debug_return_bool!(true);
    } //unsafe
}

/*
 * Allocate a struct sudoers_string, store str in it and
 * insert into the specified strlist.
 */
fn ldif_store_string(
    mut str: *const libc::c_char,
    mut strlist: *mut sudoers_str_list,
    mut sorted: bool,
) {
    let mut ls: *mut sudoers_string = std::ptr::null_mut::<sudoers_string>();
    unsafe {
        debug_decl!(SUDOERS_DEBUG_UTIL!());

        ls = sudoers_string_alloc(str);
        if ls.is_null() {
            sudo_fatalx!(
                b"%s: %s\0" as *const u8 as *const libc::c_char,
                get_function_name!(),
                b"unable to allocate memory\0" as *const u8 as *const libc::c_char
            );
        }
        if !sorted {
            (*ls).entries.stqe_next = std::ptr::null_mut::<sudoers_string>();
            *(*strlist).stqh_last = ls;
            (*strlist).stqh_last = &mut (*ls).entries.stqe_next;
        } else {
            let mut prev: *mut sudoers_string = std::ptr::null_mut::<sudoers_string>();
            let mut next: *mut sudoers_string = std::ptr::null_mut::<sudoers_string>();

            /* Insertion sort, list is small. */
            prev = (*strlist).stqh_first;
            if prev.is_null() || strcasecmp(str, (*prev).str_0) <= 0 as libc::c_int {
                (*ls).entries.stqe_next = (*strlist).stqh_first;
                if ((*ls).entries.stqe_next).is_null() {
                    (*strlist).stqh_last = &mut (*ls).entries.stqe_next;
                }
                (*strlist).stqh_first = ls;
            } else {
                loop {
                    next = (*prev).entries.stqe_next;
                    if next.is_null() {
                        break;
                    }
                    if strcasecmp(str, (*next).str_0) <= 0 as libc::c_int {
                        break;
                    }
                    prev = next;
                }
                (*ls).entries.stqe_next = (*prev).entries.stqe_next;
                if ((*ls).entries.stqe_next).is_null() {
                    (*strlist).stqh_last = &mut (*ls).entries.stqe_next;
                }
                (*prev).entries.stqe_next = ls;
            }
        }

        debug_return!();
    } //unsafe
}

/*
 * Iterator for sudo_ldap_role_to_priv().
 * Takes a pointer to a struct sudoers_string *.
 * Returns the string or NULL if we've reached the end.
 */
unsafe extern "C" fn sudoers_string_iter(mut vp: *mut *mut libc::c_void) -> *mut libc::c_char {
    let mut ls: *mut sudoers_string = *vp as *mut sudoers_string;

    if ls.is_null() {
        return std::ptr::null_mut::<libc::c_char>();
    }

    *vp = (*ls).entries.stqe_next as *mut libc::c_void;

    (*ls).str_0
}

unsafe extern "C" fn role_order_cmp(
    mut va: *const libc::c_void,
    mut vb: *const libc::c_void,
) -> libc::c_int {
    let mut a: *const sudo_role = *(va as *mut *const sudo_role);
    let mut b: *const sudo_role = *(vb as *mut *const sudo_role);
    debug_decl!(SUDOERS_DEBUG_UTIL!());

    debug_return_int!(if (*a).order < (*b).order {
        -(1 as libc::c_int)
    } else if (*a).order > (*b).order {
        1 as libc::c_int
    } else {
        0 as libc::c_int
    });
}

/*
 * Parse list of sudoOption and store in the parse tree's defaults list.
 */
fn ldif_store_options(mut parse_tree: *mut sudoers_parse_tree, mut options: *mut sudoers_str_list) {
    let mut d: *mut defaults = std::ptr::null_mut::<defaults>();
    let mut ls: *mut sudoers_string = std::ptr::null_mut::<sudoers_string>();
    let mut var: *mut libc::c_char = std::ptr::null_mut::<libc::c_char>();
    let mut val: *mut libc::c_char = std::ptr::null_mut::<libc::c_char>();
    unsafe {
        debug_decl!(SUDOERS_DEBUG_UTIL!());

        ls = (*options).stqh_first;
        while !ls.is_null() {
            d = calloc(
                1 as libc::c_int as libc::c_ulong,
                ::core::mem::size_of::<defaults>() as libc::c_ulong,
            ) as *mut defaults;
            if d.is_null() || {
                (*d).binding = malloc(::core::mem::size_of::<member_list>() as libc::c_ulong)
                    as *mut member_list;
                ((*d).binding).is_null()
            } {
                sudo_fatalx!(
                    b"%s: %s\0" as *const u8 as *const libc::c_char,
                    get_function_name!(),
                    b"unable to allocate memory\0" as *const u8 as *const libc::c_char
                );
            }
            (*(*d).binding).tqh_first = std::ptr::null_mut::<member>();
            (*(*d).binding).tqh_last = &mut (*(*d).binding).tqh_first;
            (*d).type_0 = DEFAULTS as libc::c_short;
            (*d).op = sudo_ldap_parse_option((*ls).str_0, &mut var, &mut val) as libc::c_char;
            (*d).var = strdup(var);
            if ((*d).var).is_null() {
                sudo_fatalx!(
                    b"%s: %s\0" as *const u8 as *const libc::c_char,
                    get_function_name!(),
                    b"unable to allocate memory\0" as *const u8 as *const libc::c_char
                );
            }
            if !val.is_null() {
                (*d).val = strdup(val);
                if ((*d).val).is_null() {
                    sudo_fatalx!(
                        b"%s: %s\0" as *const u8 as *const libc::c_char,
                        get_function_name!(),
                        b"unable to allocate memory\0" as *const u8 as *const libc::c_char
                    );
                }
            }
            (*d).entries.tqe_next = std::ptr::null_mut::<defaults>();
            (*d).entries.tqe_prev = (*parse_tree).defaults.tqh_last;
            *(*parse_tree).defaults.tqh_last = d;
            (*parse_tree).defaults.tqh_last = &mut (*d).entries.tqe_next;
            ls = (*ls).entries.stqe_next;
        }

        debug_return!();
    } //unsafe
}

unsafe extern "C" fn str_list_cmp(
    mut aa: *const libc::c_void,
    mut bb: *const libc::c_void,
) -> libc::c_int {
    let mut a: *const sudoers_str_list = aa as *const sudoers_str_list;
    let mut b: *const sudoers_str_list = bb as *const sudoers_str_list;
    unsafe {
        let mut lsa: *const sudoers_string = (*a).stqh_first;
        let mut lsb: *const sudoers_string = (*b).stqh_first;
        let mut ret: libc::c_int = 0;

        while !lsa.is_null() && !lsb.is_null() {
            ret = strcasecmp((*lsa).str_0, (*lsb).str_0);
            if ret != 0 as libc::c_int {
                return ret;
            }
            lsa = (*lsa).entries.stqe_next;
            lsb = (*lsb).entries.stqe_next;
        }

        if lsa == lsb {
            0 as libc::c_int
        } else if lsa.is_null() {
            -(1 as libc::c_int)
        } else {
            1 as libc::c_int
        }
    } //unsafe
}

fn str_list_cache(mut cache: *mut rbtree, mut strlistp: *mut *mut sudoers_str_list) -> libc::c_int {
    unsafe {
        let mut strlist: *mut sudoers_str_list = *strlistp;
        let mut node: *mut rbnode = std::ptr::null_mut::<rbnode>();
        let mut ret: libc::c_int = 0;
        debug_decl!(SUDOERS_DEBUG_UTIL!());

        ret = rbinsert(cache, strlist as *mut libc::c_void, &mut node);
        match ret {
            0 => {
                /* new entry, take a ref for the cache */
                (*strlist).refcnt = ((*strlist).refcnt).wrapping_add(1);
            }
            1 => {
                /* already exists, use existing and take a ref. */
                str_list_free(strlist as *mut libc::c_void);
                strlist = (*node).data as *mut sudoers_str_list;
                (*strlist).refcnt = ((*strlist).refcnt).wrapping_add(1);
                *strlistp = strlist;
            }
            _ => {}
        }
        debug_return_int!(ret);
    } //unsafe
}

/*
 * Convert a sudoRole to sudoers format and store in the parse tree.
 */
unsafe extern "C" fn role_to_sudoers(
    mut parse_tree: *mut sudoers_parse_tree,
    mut role: *mut sudo_role,
    mut store_options: bool,
    mut reuse_userspec: bool,
    mut reuse_privilege: bool,
    mut reuse_runas: bool,
) {
    let mut priv_0: *mut privilege = std::ptr::null_mut::<privilege>();
    let mut ls: *mut sudoers_string = std::ptr::null_mut::<sudoers_string>();
    let mut us: *mut userspec = std::ptr::null_mut::<userspec>();
    let mut m: *mut member = std::ptr::null_mut::<member>();
    debug_decl!(SUDOERS_DEBUG_UTIL!());

    /*
     * TODO: use cn to create a UserAlias if multiple users in it?
     */

    if reuse_userspec {
        /* Re-use the previous userspec */
        us = *(*((*parse_tree).userspecs.tqh_last as *mut userspec_list)).tqh_last;
    } else {
        /* Allocate a new userspec and fill in the user list. */
        us = calloc(
            1 as libc::c_int as libc::c_ulong,
            ::core::mem::size_of::<userspec>() as libc::c_ulong,
        ) as *mut userspec;
        if us.is_null() {
            sudo_fatalx!(
                b"%s: %s\0" as *const u8 as *const libc::c_char,
                get_function_name!(),
                b"unable to allocate memory\0" as *const u8 as *const libc::c_char
            );
        }
        (*us).privileges.tqh_first = std::ptr::null_mut::<privilege>();
        (*us).privileges.tqh_last = &mut (*us).privileges.tqh_first;
        (*us).users.tqh_first = std::ptr::null_mut::<member>();
        (*us).users.tqh_last = &mut (*us).users.tqh_first;
        (*us).comments.stqh_first = std::ptr::null_mut::<sudoers_comment>();
        (*us).comments.stqh_last = &mut (*us).comments.stqh_first;

        ls = (*(*role).users).stqh_first;
        while !ls.is_null() {
            let mut user: *mut libc::c_char = (*ls).str_0;
            m = calloc(
                1 as libc::c_int as libc::c_ulong,
                ::core::mem::size_of::<member>() as libc::c_ulong,
            ) as *mut member;
            if m.is_null() {
                sudo_fatalx!(
                    b"%s: %s\0" as *const u8 as *const libc::c_char,
                    get_function_name!(),
                    b"unable to allocate memory\0" as *const u8 as *const libc::c_char
                );
            }
            (*m).negated = sudo_ldap_is_negated(&mut user) as libc::c_short;
            (*m).name = strdup(user);
            if ((*m).name).is_null() {
                sudo_fatalx!(
                    b"%s: %s\0" as *const u8 as *const libc::c_char,
                    get_function_name!(),
                    b"unable to allocate memory\0" as *const u8 as *const libc::c_char
                );
            }
            if strcmp(user, b"ALL\0" as *const u8 as *const libc::c_char) == 0 as libc::c_int {
                (*m).type0 = ALL as libc::c_short;
            } else if *user as libc::c_int == '+' as i32 {
                (*m).type0 = NETGROUP as libc::c_short;
            } else if *user as libc::c_int == '%' as i32 {
                (*m).type0 = USERGROUP as libc::c_short;
            } else {
                (*m).type0 = WORD as libc::c_short;
            }
            (*m).entries.tqe_next = std::ptr::null_mut::<member>();
            (*m).entries.tqe_prev = (*us).users.tqh_last;
            *(*us).users.tqh_last = m;
            (*us).users.tqh_last = &mut (*m).entries.tqe_next;
            ls = (*ls).entries.stqe_next;
        }
    }

    /* Add source role as a comment. */
    if !((*role).cn).is_null() {
        let mut comment: *mut sudoers_comment = std::ptr::null_mut::<sudoers_comment>();
        if reuse_userspec {
            /* Try to re-use comment too. */
            while !comment.is_null() {
                if strncasecmp(
                    (*comment).str_0,
                    b"sudoRole \0" as *const u8 as *const libc::c_char,
                    9 as libc::c_int as libc::c_ulong,
                ) == 0 as libc::c_int
                {
                    let mut tmpstr: *mut libc::c_char = std::ptr::null_mut::<libc::c_char>();
                    if asprintf(
                        &mut tmpstr as *mut *mut libc::c_char,
                        b"%s, %s\0" as *const u8 as *const libc::c_char,
                        (*comment).str_0,
                        (*role).cn,
                    ) == -(1 as libc::c_int)
                    {
                        sudo_fatalx!(
                            b"%s: %s\0" as *const u8 as *const libc::c_char,
                            get_function_name!(),
                            b"unable to allocate memory\0" as *const u8 as *const libc::c_char
                        );
                    }
                    free((*comment).str_0 as *mut libc::c_void);
                    (*comment).str_0 = tmpstr;
                    break;
                } else {
                    comment = (*comment).entries.stqe_next;
                }
            }
        }
        if comment.is_null() {
            /* Create a new comment. */
            comment = malloc(::core::mem::size_of::<sudoers_comment>() as libc::c_ulong)
                as *mut sudoers_comment;
            if comment.is_null() {
                sudo_fatalx!(
                    b"%s: %s\0" as *const u8 as *const libc::c_char,
                    get_function_name!(),
                    b"unable to allocate memory\0" as *const u8 as *const libc::c_char
                );
            }
            if asprintf(
                &mut (*comment).str_0 as *mut *mut libc::c_char,
                b"sudoRole %s\0" as *const u8 as *const libc::c_char,
                (*role).cn,
            ) == -(1 as libc::c_int)
            {
                sudo_fatalx!(
                    b"%s: %s\0" as *const u8 as *const libc::c_char,
                    get_function_name!(),
                    b"unable to allocate memory\0" as *const u8 as *const libc::c_char
                );
            }
            (*comment).entries.stqe_next = std::ptr::null_mut::<sudoers_comment>();
            *(*us).comments.stqh_last = comment;
            (*us).comments.stqh_last = &mut (*comment).entries.stqe_next;
        }
    }

    /* Convert role to sudoers privilege. */
    priv_0 = sudo_ldap_role_to_priv(
        (*role).cn,
        (*(*role).hosts).stqh_first as *mut libc::c_void,
        (*(*role).runasusers).stqh_first as *mut libc::c_void,
        (*(*role).runasgroups).stqh_first as *mut libc::c_void,
        (*(*role).cmnds).stqh_first as *mut libc::c_void,
        (*(*role).options).stqh_first as *mut libc::c_void,
        (*role).notbefore,
        (*role).notafter,
        true,
        store_options,
        Some(
            sudoers_string_iter
                as unsafe extern "C" fn(*mut *mut libc::c_void) -> *mut libc::c_char,
        ),
    );
    if priv_0.is_null() {
        sudo_fatalx!(
            b"%s: %s\0" as *const u8 as *const libc::c_char,
            get_function_name!(),
            b"unable to allocate memory\0" as *const u8 as *const libc::c_char
        );
    }

    if reuse_privilege {
        /* Hostspec unchanged, append cmndlist to previous privilege. */
        let mut prev_priv: *mut privilege =
            *(*((*us).privileges.tqh_last as *mut privilege_list)).tqh_last;
        if reuse_runas {
            /* Runas users and groups same if as in previous privilege. */
            let mut runasuserlist: *mut member_list =
                (*(*prev_priv).cmndlist.tqh_first).runasuserlist;
            let mut runasgrouplist: *mut member_list =
                (*(*prev_priv).cmndlist.tqh_first).runasgrouplist;
            let mut cmndspec: *mut cmndspec = (*priv_0).cmndlist.tqh_first;

            /* Free duplicate runas lists. */
            if !((*cmndspec).runasuserlist).is_null() {
                free_members((*cmndspec).runasuserlist);
                free((*cmndspec).runasuserlist as *mut libc::c_void);
            }
            if !((*cmndspec).runasgrouplist).is_null() {
                free_members((*cmndspec).runasgrouplist);
                free((*cmndspec).runasgrouplist as *mut libc::c_void);
            }

            /* Update cmndspec with previous runas lists. */
            cmndspec = (*priv_0).cmndlist.tqh_first;
            while !cmndspec.is_null() {
                (*cmndspec).runasuserlist = runasuserlist;
                (*cmndspec).runasgrouplist = runasgrouplist;
                cmndspec = (*cmndspec).entries.tqe_next;
            }
        }
        if !((*priv_0).cmndlist.tqh_first).is_null() {
            *(*prev_priv).cmndlist.tqh_last = (*priv_0).cmndlist.tqh_first;
            (*(*priv_0).cmndlist.tqh_first).entries.tqe_prev = (*prev_priv).cmndlist.tqh_last;
            (*prev_priv).cmndlist.tqh_last = (*priv_0).cmndlist.tqh_last;
            (*priv_0).cmndlist.tqh_first = std::ptr::null_mut::<cmndspec>();
            (*priv_0).cmndlist.tqh_last = &mut (*priv_0).cmndlist.tqh_first;
        }
        free_privilege(priv_0);
    } else {
        (*priv_0).entries.tqe_next = std::ptr::null_mut::<privilege>();
        (*priv_0).entries.tqe_prev = (*us).privileges.tqh_last;
        *(*us).privileges.tqh_last = priv_0;
        (*us).privileges.tqh_last = &mut (*priv_0).entries.tqe_next;
    }

    /* Add finished userspec to the list if new. */
    if !reuse_userspec {
        (*us).entries.tqe_next = std::ptr::null_mut::<userspec>();
        (*us).entries.tqe_prev = (*parse_tree).userspecs.tqh_last;
        *(*parse_tree).userspecs.tqh_last = us;
        (*parse_tree).userspecs.tqh_last = &mut (*us).entries.tqe_next;
    }

    debug_return!();
}

/*
 * Convert the list of sudoRoles to sudoers format and store in the parse tree.
 */
unsafe extern "C" fn ldif_to_sudoers(
    mut parse_tree: *mut sudoers_parse_tree,
    mut roles: *mut sudo_role_list,
    mut numroles: libc::c_uint,
    mut store_options: bool,
) {
    let mut role_array: *mut *mut sudo_role = std::ptr::null_mut::<*mut sudo_role>();
    let mut role: *mut sudo_role = std::ptr::null_mut::<sudo_role>();
    let mut n: libc::c_uint = 0;
    debug_decl!(SUDOERS_DEBUG_UTIL!());

    /* Convert from list of roles to array and sort by order. */
    role_array = reallocarray(
        std::ptr::null_mut::<libc::c_void>(),
        numroles.wrapping_add(1 as libc::c_int as libc::c_uint) as size_t,
        ::core::mem::size_of::<*mut sudo_role>() as libc::c_ulong,
    ) as *mut *mut sudo_role;
    n = 0 as libc::c_int as libc::c_uint;
    while n < numroles {
        role = (*roles).stqh_first;
        if role.is_null() {
            break; /* cannot happen */
        }
        (*roles).stqh_first = (*(*roles).stqh_first).entries.stqe_next;
        if ((*roles).stqh_first).is_null() {
            (*roles).stqh_last = &mut (*roles).stqh_first;
        }
        let fresh1 = &mut *role_array.offset(n as isize);
        *fresh1 = role;
        n = n.wrapping_add(1);
    }
    let fresh2 = &mut *role_array.offset(n as isize);
    *fresh2 = std::ptr::null_mut::<sudo_role>();
    qsort(
        role_array as *mut libc::c_void,
        numroles as size_t,
        ::core::mem::size_of::<*mut sudo_role>() as libc::c_ulong,
        Some(
            role_order_cmp
                as unsafe extern "C" fn(*const libc::c_void, *const libc::c_void) -> libc::c_int,
        ),
    );
    n = 0 as libc::c_int as libc::c_uint;
    while n < numroles {
        let mut reuse_userspec: bool = false;
        let mut reuse_privilege: bool = false;
        let mut reuse_runas: bool = false;

        role = *role_array.offset(n as isize);

        /* Check whether we can reuse the previous user and host specs */
        if n > 0 as libc::c_int as libc::c_uint
            && (*role).users
                == (**role_array.offset(n.wrapping_sub(1 as libc::c_int as libc::c_uint) as isize))
                    .users
        {
            reuse_userspec = true;

            /*
             * Since options are stored per-privilege we can't
             * append to the previous privilege's cmndlist if
             * we are storing options.
             */
            if !store_options
                && (*role).hosts
                    == (**role_array
                        .offset(n.wrapping_sub(1 as libc::c_int as libc::c_uint) as isize))
                    .hosts
            {
                reuse_privilege = true;

                /* Reuse runasusers and runasgroups if possible. */
                if (*role).runasusers
                    == (**role_array
                        .offset(n.wrapping_sub(1 as libc::c_int as libc::c_uint) as isize))
                    .runasusers
                    && (*role).runasgroups
                        == (**role_array
                            .offset(n.wrapping_sub(1 as libc::c_int as libc::c_uint) as isize))
                        .runasgroups
                {
                    reuse_runas = true;
                }
            }

            /*if !store_options {
                if (*role).hosts
                    == (**role_array
                        .offset(n.wrapping_sub(1 as libc::c_int as libc::c_uint) as isize))
                    .hosts
                {
                    reuse_privilege = true;

                    /* Reuse runasusers and runasgroups if possible. */
                    if (*role).runasusers
                        == (**role_array
                            .offset(n.wrapping_sub(1 as libc::c_int as libc::c_uint) as isize))
                        .runasusers
                        && (*role).runasgroups
                            == (**role_array
                                .offset(n.wrapping_sub(1 as libc::c_int as libc::c_uint) as isize))
                            .runasgroups
                    {
                        reuse_runas = true;
                    }
                }
            }*/
        }
        role_to_sudoers(
            parse_tree,
            role,
            store_options,
            reuse_userspec,
            reuse_privilege,
            reuse_runas,
        );
        n = n.wrapping_add(1);
    }

    /* Clean up. */
    n = 0 as libc::c_int as libc::c_uint;
    while n < numroles {
        sudo_role_free(*role_array.offset(n as isize));
        n = n.wrapping_add(1);
    }
    free(role_array as *mut libc::c_void);

    debug_return!();
}

/*
 * Given a cn with possible quoted characters, return a copy of
 * the cn with quote characters ('\\') removed.
 * The caller is responsible for freeing the returned string.
 */
fn unquote_cn(mut src: *const libc::c_char) -> *mut libc::c_char {
    let mut dst: *mut libc::c_char = std::ptr::null_mut::<libc::c_char>();
    let mut new_cn: *mut libc::c_char = std::ptr::null_mut::<libc::c_char>();
    let mut len: size_t = 0;
    unsafe {
        debug_decl!(SUDOERS_DEBUG_UTIL!());

        len = strlen(src);
        new_cn = malloc(len.wrapping_add(1 as libc::c_int as libc::c_ulong)) as *mut libc::c_char;
        if new_cn.is_null() {
            debug_return_str!(std::ptr::null_mut::<libc::c_char>());
        }
        dst = new_cn;
        while *src as libc::c_int != '\0' as i32 {
            if *src.offset(0 as libc::c_int as isize) as libc::c_int == '\\' as i32
                && *src.offset(1 as libc::c_int as isize) as libc::c_int != '\0' as i32
            {
                src = src.offset(1);
            }
            let fresh3 = src;
            src = src.offset(1);
            let fresh4 = dst;
            dst = dst.offset(1);
            *fresh4 = *fresh3;
        }
        *dst = '\0' as i32 as libc::c_char;

        debug_return_str!(new_cn);
    } //unsafe
}

/*
 * Parse a sudoers file in LDIF format, https://tools.ietf.org/html/rfc2849
 * Parsed sudoRole objects are stored in the specified parse_tree which
 * must already be initialized.
 */
#[no_mangle]
pub fn sudoers_parse_ldif(
    mut parse_tree: *mut sudoers_parse_tree,
    mut fp: *mut FILE,
    mut sudoers_base: *const libc::c_char,
    mut store_options: bool,
) -> bool {
    let mut roles: sudo_role_list = sudo_role_list {
        stqh_first: std::ptr::null_mut::<sudo_role>(),
        stqh_last: std::ptr::null_mut::<*mut sudo_role>(),
    };
    roles = {
        sudo_role_list {
            stqh_first: std::ptr::null_mut::<sudo_role>(),
            stqh_last: &mut roles.stqh_first,
        }
        //init
    };
    let mut role: *mut sudo_role = std::ptr::null_mut::<sudo_role>();
    let mut usercache: *mut rbtree = std::ptr::null_mut::<rbtree>();
    let mut groupcache: *mut rbtree = std::ptr::null_mut::<rbtree>();
    let mut hostcache: *mut rbtree = std::ptr::null_mut::<rbtree>();
    let mut numroles: libc::c_uint = 0 as libc::c_int as libc::c_uint;
    let mut in_role: bool = false;
    let mut linesize: size_t = 0 as libc::c_int as size_t;
    let mut attr: *mut libc::c_char = std::ptr::null_mut::<libc::c_char>();
    let mut name: *mut libc::c_char = std::ptr::null_mut::<libc::c_char>();
    let mut line: *mut libc::c_char = std::ptr::null_mut::<libc::c_char>();
    let mut savedline: *mut libc::c_char = std::ptr::null_mut::<libc::c_char>();
    let mut savedlen: ssize_t = 0 as libc::c_int as ssize_t;
    let mut mismatch: bool = false;
    let mut errors: libc::c_int = 0 as libc::c_int;
    unsafe {
        debug_decl!(SUDOERS_DEBUG_UTIL!());

        /* Free old contents of the parse tree (if any). */
        free_parse_tree(parse_tree);

        /*
         * We cache user, group and host lists to make it eay to detect when there
         * are identical lists (simple pointer compare).  This makes it possible
         * to merge multiplpe sudoRole objects into a single UserSpec and/or
         * Privilege.  The lists are sorted since LDAP order is arbitrary.
         */
        usercache = rbcreate(Some(
            str_list_cmp
                as unsafe extern "C" fn(*const libc::c_void, *const libc::c_void) -> libc::c_int,
        ));
        groupcache = rbcreate(Some(
            str_list_cmp
                as unsafe extern "C" fn(*const libc::c_void, *const libc::c_void) -> libc::c_int,
        ));
        hostcache = rbcreate(Some(
            str_list_cmp
                as unsafe extern "C" fn(*const libc::c_void, *const libc::c_void) -> libc::c_int,
        ));
        if usercache.is_null() || groupcache.is_null() || hostcache.is_null() {
            sudo_fatalx!(
                b"%s: %s\0" as *const u8 as *const libc::c_char,
                get_function_name!(),
                b"unable to allocate memory\0" as *const u8 as *const libc::c_char
            );
        }

        /* Read through input, parsing into sudo_roles and global defaults. */
        loop {
            let mut ch: libc::c_int = 0;
            let mut len: ssize_t = getdelim(&mut line, &mut linesize, '\n' as i32, fp);

            /* Trim trailing return or newline. */
            while len > 0 as libc::c_int as libc::c_long
                && (*line.offset((len - 1 as libc::c_int as libc::c_long) as isize) as libc::c_int
                    == '\r' as i32
                    || *line.offset((len - 1 as libc::c_int as libc::c_long) as isize)
                        as libc::c_int
                        == '\n' as i32)
            {
                len -= 1;
                *line.offset(len as isize) = '\0' as i32 as libc::c_char;
            }

            /* Blank line or EOF terminates an entry. */
            if len <= 0 as libc::c_int as libc::c_long {
                if in_role {
                    if !((*role).cn).is_null()
                        && strcasecmp(
                            (*role).cn,
                            b"defaults\0" as *const u8 as *const libc::c_char,
                        ) == 0 as libc::c_int
                    {
                        ldif_store_options(parse_tree, (*role).options);
                        sudo_role_free(role);
                    } else if ((*(*role).users).stqh_first).is_null()
                        || ((*(*role).hosts).stqh_first).is_null()
                        || ((*(*role).cmnds).stqh_first).is_null()
                    {
                        sudo_warnx!(
                            b"ignoring incomplete sudoRole: cn: %s\0" as *const u8
                                as *const libc::c_char,
                            if !((*role).cn).is_null() {
                                (*role).cn as *const libc::c_char
                            } else {
                                b"UNKNOWN\0" as *const u8 as *const libc::c_char
                            }
                        );
                        sudo_role_free(role);
                    } else {
                        /* Cache users, hosts, runasusers and runasgroups. */
                        if str_list_cache(usercache, &mut (*role).users) == -(1 as libc::c_int)
                            || str_list_cache(hostcache, &mut (*role).hosts) == -(1 as libc::c_int)
                            || str_list_cache(usercache, &mut (*role).runasusers)
                                == -(1 as libc::c_int)
                            || str_list_cache(groupcache, &mut (*role).runasgroups)
                                == -(1 as libc::c_int)
                        {
                            sudo_fatalx!(
                                b"%s: %s\0" as *const u8 as *const libc::c_char,
                                get_function_name!(),
                                b"unable to allocate memory\0" as *const u8 as *const libc::c_char
                            );
                        }

                        /* Store finished role. */
                        (*role).entries.stqe_next = std::ptr::null_mut::<sudo_role>();
                        *roles.stqh_last = role;
                        roles.stqh_last = &mut (*role).entries.stqe_next;
                        numroles = numroles.wrapping_add(1);
                    }
                    role = std::ptr::null_mut::<sudo_role>();
                    in_role = false;
                }
                if len == -(1 as libc::c_int) as libc::c_long {
                    /* EOF */
                    break;
                }
                mismatch = false;
            } else {
                if !savedline.is_null() {
                    let mut tmp: *mut libc::c_char = std::ptr::null_mut::<libc::c_char>();

                    /* Append to saved line. */
                    linesize = (savedlen + len + 1 as libc::c_int as libc::c_long) as size_t;
                    tmp = realloc(savedline as *mut libc::c_void, linesize) as *mut libc::c_char;
                    if tmp.is_null() {
                        sudo_fatalx!(
                            b"%s: %s\0" as *const u8 as *const libc::c_char,
                            get_function_name!(),
                            b"unable to allocate memory\0" as *const u8 as *const libc::c_char
                        );
                    }
                    memcpy(
                        tmp.offset(savedlen as isize) as *mut libc::c_void,
                        line as *const libc::c_void,
                        (len + 1 as libc::c_int as libc::c_long) as libc::c_ulong,
                    );
                    free(line as *mut libc::c_void);
                    line = tmp;
                    savedline = std::ptr::null_mut::<libc::c_char>();
                }

                /* Check for folded line */
                ch = getc(fp);
                if ch == ' ' as i32 {
                    /* folded line, append to the saved portion. */
                    savedlen = len;
                    savedline = line;
                    line = std::ptr::null_mut::<libc::c_char>();
                    linesize = 0 as libc::c_int as size_t;
                } else {
                    ungetc(ch, fp); /* not folded, push back ch */

                    /* Skip comment lines or records that don't match the base. */
                    if *line as libc::c_int == '#' as i32 || mismatch as libc::c_int != 0 {
                        continue;
                    }

                    /* Reject invalid LDIF. */
                    if !ldif_parse_attribute(line, &mut name, &mut attr) {
                        sudo_warnx!(
                            b"invalid LDIF attribute: %s\0" as *const u8 as *const libc::c_char,
                            line
                        );
                        errors += 1;
                    } else {
                        /* Parse dn and objectClass. */
                        if strcasecmp(name, b"dn\0" as *const u8 as *const libc::c_char)
                            == 0 as libc::c_int
                        {
                            /* Compare dn to base, if specified. */
                            if !sudoers_base.is_null() {
                                /* Skip over cn if present. */
                                if strncasecmp(
                                    attr,
                                    b"cn=\0" as *const u8 as *const libc::c_char,
                                    3 as libc::c_int as libc::c_ulong,
                                ) == 0 as libc::c_int
                                {
                                    attr = attr.offset(3 as libc::c_int as isize);
                                    while *attr as libc::c_int != '\0' as i32 {
                                        /* Handle escaped ',' chars. */
                                        if *attr as libc::c_int == '\\' as i32 {
                                            attr = attr.offset(1);
                                        }
                                        if *attr as libc::c_int == ',' as i32 {
                                            attr = attr.offset(1);
                                            break;
                                        } else {
                                            attr = attr.offset(1);
                                        }
                                    }
                                }
                                if strcasecmp(attr, sudoers_base) != 0 as libc::c_int {
                                    /* Doesn't match base, skip the rest of it. */
                                    mismatch = true;
                                    continue;
                                }
                            }
                        } else if strcasecmp(
                            name,
                            b"objectClass\0" as *const u8 as *const libc::c_char,
                        ) == 0 as libc::c_int
                            && strcasecmp(attr, b"sudoRole\0" as *const u8 as *const libc::c_char)
                                == 0 as libc::c_int
                        {
                            if role.is_null() {
                                role = sudo_role_alloc();
                                if role.is_null() {
                                    sudo_fatalx!(
                                        b"%s: %s\0" as *const u8 as *const libc::c_char,
                                        get_function_name!(),
                                        b"unable to allocate memory\0" as *const u8
                                            as *const libc::c_char
                                    );
                                }
                            }
                            in_role = true;
                        }
                        /*} else if strcasecmp(
                            name,
                            b"objectClass\0" as *const u8 as *const libc::c_char,
                        ) == 0 as libc::c_int
                        {
                            if strcasecmp(attr, b"sudoRole\0" as *const u8 as *const libc::c_char)
                                == 0 as libc::c_int
                            {
                                if role.is_null() {
                                    role = sudo_role_alloc();
                                    if role.is_null() {
                                        sudo_fatalx!(
                                            b"%s: %s\0" as *const u8 as *const libc::c_char,
                                            get_function_name!(),
                                            b"unable to allocate memory\0" as *const u8
                                                as *const libc::c_char
                                        );
                                    }
                                }
                                in_role = true;
                            }
                        }*/
                        /* Not in a sudoRole, keep reading. */
                        if !in_role {
                            continue;
                        }
                        if strcasecmp(name, b"cn\0" as *const u8 as *const libc::c_char)
                            == 0 as libc::c_int
                        {
                            free((*role).cn as *mut libc::c_void);
                            (*role).cn = unquote_cn(attr);
                            if ((*role).cn).is_null() {
                                sudo_fatalx!(
                                    b"%s: %s\0" as *const u8 as *const libc::c_char,
                                    get_function_name!(),
                                    b"unable to allocate memory\0" as *const u8
                                        as *const libc::c_char
                                );
                            }
                        } else if strcasecmp(
                            name,
                            b"sudoUser\0" as *const u8 as *const libc::c_char,
                        ) == 0 as libc::c_int
                        {
                            ldif_store_string(attr, (*role).users, 1 as libc::c_int != 0);
                        } else if strcasecmp(
                            name,
                            b"sudoHost\0" as *const u8 as *const libc::c_char,
                        ) == 0 as libc::c_int
                        {
                            ldif_store_string(attr, (*role).hosts, 1 as libc::c_int != 0);
                        } else if strcasecmp(
                            name,
                            b"sudoRunAs\0" as *const u8 as *const libc::c_char,
                        ) == 0 as libc::c_int
                        {
                            ldif_store_string(attr, (*role).runasusers, 1 as libc::c_int != 0);
                        } else if strcasecmp(
                            name,
                            b"sudoRunAsUser\0" as *const u8 as *const libc::c_char,
                        ) == 0 as libc::c_int
                        {
                            ldif_store_string(attr, (*role).runasusers, 1 as libc::c_int != 0);
                        } else if strcasecmp(
                            name,
                            b"sudoRunAsGroup\0" as *const u8 as *const libc::c_char,
                        ) == 0 as libc::c_int
                        {
                            ldif_store_string(attr, (*role).runasgroups, 1 as libc::c_int != 0);
                        } else if strcasecmp(
                            name,
                            b"sudoCommand\0" as *const u8 as *const libc::c_char,
                        ) == 0 as libc::c_int
                        {
                            ldif_store_string(attr, (*role).cmnds, 0 as libc::c_int != 0);
                        } else if strcasecmp(
                            name,
                            b"sudoOption\0" as *const u8 as *const libc::c_char,
                        ) == 0 as libc::c_int
                        {
                            ldif_store_string(attr, (*role).options, 0 as libc::c_int != 0);
                        } else if strcasecmp(
                            name,
                            b"sudoOrder\0" as *const u8 as *const libc::c_char,
                        ) == 0 as libc::c_int
                        {
                            let mut ep: *mut libc::c_char = std::ptr::null_mut::<libc::c_char>();
                            (*role).order = strtod(attr, &mut ep);
                            if ep == attr || *ep as libc::c_int != '\0' as i32 {
                                sudo_warnx!(
                                    b"invalid sudoOrder attribute: %s\0" as *const u8
                                        as *const libc::c_char,
                                    attr
                                );
                                errors += 1;
                            }
                        } else if strcasecmp(
                            name,
                            b"sudoNotBefore\0" as *const u8 as *const libc::c_char,
                        ) == 0 as libc::c_int
                        {
                            free((*role).notbefore as *mut libc::c_void);
                            (*role).notbefore = strdup(attr);
                            if ((*role).notbefore).is_null() {
                                sudo_fatalx!(
                                    b"%s: %s\0" as *const u8 as *const libc::c_char,
                                    get_function_name!(),
                                    b"unable to allocate memory\0" as *const u8
                                        as *const libc::c_char
                                );
                            }
                        } else if strcasecmp(
                            name,
                            b"sudoNotAfter\0" as *const u8 as *const libc::c_char,
                        ) == 0 as libc::c_int
                        {
                            free((*role).notafter as *mut libc::c_void);
                            (*role).notafter = strdup(attr);
                            if ((*role).notafter).is_null() {
                                sudo_fatalx!(
                                    b"%s: %s\0" as *const u8 as *const libc::c_char,
                                    get_function_name!(),
                                    b"unable to allocate memory\0" as *const u8
                                        as *const libc::c_char
                                );
                            }
                        }
                    }
                }
            }
        }
        sudo_role_free(role);
        free(line as *mut libc::c_void);

        /* Convert from roles to sudoers data structures. */
        ldif_to_sudoers(parse_tree, &mut roles, numroles, store_options);

        /* Clean up. */
        rbdestroy(
            usercache,
            Some(str_list_free as unsafe extern "C" fn(*mut libc::c_void) -> ()),
        );
        rbdestroy(
            groupcache,
            Some(str_list_free as unsafe extern "C" fn(*mut libc::c_void) -> ()),
        );
        rbdestroy(
            hostcache,
            Some(str_list_free as unsafe extern "C" fn(*mut libc::c_void) -> ()),
        );

        if fp != stdin {
            fclose(fp);
        }

        debug_return_bool!(errors == 0);
    } //unsafe
}
